package com.danco.training.zaneuskaya.service.impl;

import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import com.danco.training.zaneuskaya.annotation.ConfigProperty;
import com.danco.training.zaneuskaya.annotation.ConfigPropertyAnalizer;
import com.danco.training.zaneuskaya.annotation.Type;
import com.danco.training.zaneuskaya.api.BookService;
import com.danco.training.zaneuskaya.cswworker.CSVReader;
import com.danco.training.zaneuskaya.cswworker.CSVSaver;
import com.danco.training.zaneuskaya.cswworker.Separator;
import com.danco.training.zaneuskaya.domain.Book;
import com.danco.training.zaneuskaya.domain.Claim;
import com.danco.training.zaneuskaya.service.comparator.BookComparatorByAlphabet;
import com.danco.training.zaneuskaya.service.comparator.BookComparatorByPrice;
import com.danco.training.zaneuskaya.service.comparator.BookComparatorByPublicationDate;
import com.danco.training.zaneuskaya.service.comparator.BookComparatorByQuantity;
import com.danco.training.zaneuskaya.service.comparator.BookComparatorByReceiptDate;
import com.danco.training.zaneuskaya.service.storage.BookStorage;
import com.danco.training.zaneuskaya.service.storage.ClaimStorage;

public class BookServiceImpl implements Serializable, BookService {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2536598046985962659L;

	private BookStorage bookStorage;
	private ClaimStorage claimStorage;

	@ConfigProperty(configName = { "bookshop.properties" }, propertyName = "months", type = Type.INTEGER)
	public static int MONTH_NUMBER;

	@ConfigProperty(configName = { "bookshop.properties" }, propertyName = "setclaim", type = Type.BOOLEAN)
	public static boolean EXECUTE_CLAIM;

	public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
			"yyyy.MM.dd");

	private BookComparatorByAlphabet bookComparatorByAlphabet = new BookComparatorByAlphabet();
	private BookComparatorByPublicationDate bookComparatorByPublicationDate = new BookComparatorByPublicationDate();
	private BookComparatorByPrice bookComparatorByPrice = new BookComparatorByPrice();
	private BookComparatorByQuantity bookComparatorByQuantity = new BookComparatorByQuantity();
	private BookComparatorByReceiptDate bookComparatorByReceiptDate = new BookComparatorByReceiptDate();

	private static final Logger logger = Logger
			.getLogger(BookServiceImpl.class);
	private static BookServiceImpl bookStorageSeviceImplInstance;

	public BookServiceImpl() {
		ConfigPropertyAnalizer.configPropertyAnalizer(this);
		this.bookStorage = BookStorage.getBookStorageInstance();
		this.claimStorage = ClaimStorage.getClaimStorageInstance();
	}

	public Book getById(int id) {

		for (Book b : bookStorage.getBooks()) {
			if (b.getId() == id) {
				logger.trace("Book is founded in Bookstorage by id");
				return b;
			}

		}
		logger.info("Book not founded in Bookstorage ");
		return null;
	}

	public List<Book> getList() {
		List<Book> res = new ArrayList<Book>(bookStorage.getBooks());

		logger.trace("Book list");

		return res;
	}

	public List<Integer> getListId() {
		List<Integer> res = new ArrayList<Integer>();
		for (Book b : bookStorage.getBooks()) {
			res.add(b.getId());
		}

		return res;

	}

	public int generateUnexistedId() {

		int id0 = 1;
		while (getListId().contains(id0)) {
			id0++;
		}
		return id0;
	}

	@SuppressWarnings("deprecation")
	public List<Book> getSomeMonthsUnsoldBooks(List<Book> books) {
		List<Book> res = new ArrayList<Book>();
		for (Book b : books) {
			if (b.getReceiptDate().getMonth() - new Date().getMonth() >= MONTH_NUMBER) {
				if (b.getQuantity() > 0) {

					res.add(b);

				}
			}
		}
		logger.trace("MONTH_NUMBER " + " months unsold books are founded");
		return res;

	}

	public List<Book> sortBooksByAlphabet(List<Book> books) {

		Collections.sort(books, bookComparatorByAlphabet);
		logger.trace("Books are sorted by alphabet");
		return books;
	}

	public List<Book> sortBooksByPublicationDate(List<Book> books) {

		Collections.sort(books, bookComparatorByPublicationDate);
		logger.trace("Books are sorted by publication date");
		return books;
	}

	public List<Book> sortBooksByPrice(List<Book> books) {

		Collections.sort(books, bookComparatorByPrice);
		logger.trace("Books are sorted by price");
		return books;
	}

	public List<Book> sortBooksByAvailability(List<Book> books) {

		Collections.sort(books, bookComparatorByQuantity);
		logger.trace("Books are sorted by availability");

		return books;
	}

	public List<Book> sortUnsoldBooksByReceiptDate(List<Book> books) {
		List<Book> res = new ArrayList<Book>();
		res = getSomeMonthsUnsoldBooks(books);
		Collections.sort(res, bookComparatorByReceiptDate);
		logger.trace("Unsold books are sorted by receipt date");

		return res;
	}

	public List<Book> sortUnsoldBooksByPrice(List<Book> books) {
		List<Book> res = new ArrayList<Book>();
		res = getSomeMonthsUnsoldBooks(books);
		Collections.sort(res, bookComparatorByPrice);
		logger.trace("Some months unsold books are sorted by price");

		return res;
	}

	public List<Book> getAvailableBooks() {
		List<Book> books = new ArrayList<Book>();
		for (Book book : books) {
			if (book.isAvailable()) {
				books.add(book);
			}
		}
		return books;
	}

	public List<Book> getNotAvailableBooks() {
		List<Book> books = new ArrayList<Book>();
		for (Book book : books) {
			if (!book.isAvailable()) {
				books.add(book);
			}
		}
		return books;
	}

	public boolean addEntity(Book book) {

		try {
			if (bookStorage.getBooks().contains(book)) {

				Book b = getById(book.getId());
				book.setQuantity(b.getQuantity() + 1);
				updateEntity(b, book);
				logger.info("Instance of the existing book is added to the BookStorage");

			} else {
				book.setQuantity(1);
				book.setReceiptDate(new Date());
				book.setAvailable(true);
				ArrayList<Book> books = new ArrayList<Book>(
						bookStorage.getBooks());
				books.add(book);
				bookStorage.setBooks(books);
				logger.trace("Instance of the new book is added to the BookStorage");
			}
			if (EXECUTE_CLAIM) {
				for (Claim c : claimStorage.getClaims()) {

					if (c.getBook().getId() == book.getId()) {
						c.setExecuted(true);
					}
				}
			}
			return true;

		}

		catch (Exception e) {
			logger.error(e.getMessage(), e);
			return false;
		}

	}

	public boolean updateEntity(Book book1, Book book2) {
		book2.setId(book1.getId());
		removeEntityById(book1.getId());
		return addEntity(book2);
	}

	public boolean removeEntity(Book book) {
		if (getById(book.getId()) != null) {
			return bookStorage.getBooks().remove(book);
		}

		return false;
	}

	public boolean removeEntityById(int id) {
		if (getById(id) != null) {
			return bookStorage.getBooks().remove(getById(id));
		}

		return false;
	}

	public void saveEntityListToFile(String file) {
		ArrayList<String> res = new ArrayList<String>();
		for (Book b : getList()) {
			res.add(entityDetails(b));
		}
		try {
			CSVSaver.saveListToFile(file, res);
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

	public List<Book> readEntityList(String file) {
		ArrayList<Book> res = new ArrayList<Book>();

		Date date = new Date();

		for (int i = 0; i < CSVReader.readFile(file).length; i++) {
			String book[] = CSVReader.readFile(file)[i]
					.split(Separator.SEPARATOR.getS());
			try {
				date = DATE_FORMAT.parse(book[3]);
			} catch (ParseException e) {
				logger.error(e.getMessage(), e);
			}
			Book b = new Book(Integer.parseInt(book[0]), book[1], book[2], date);
			res.add(b);

		}
		return res;
	}

	public static BookServiceImpl getBookStorageSeviceImplInstance() {

		if (bookStorageSeviceImplInstance == null) {
			bookStorageSeviceImplInstance = new BookServiceImpl();

		}
		return bookStorageSeviceImplInstance;
	}

	public String entityDetails(Book t) {

		StringBuffer str = new StringBuffer();

		str.append(t.getId());
		str.append(Separator.SEPARATOR.getS());
		str.append(t.getTitle());
		str.append(Separator.SEPARATOR.getS());
		str.append(t.getAuthor());
		str.append(Separator.SEPARATOR.getS());
		str.append(DATE_FORMAT.format(t.getPublicationDate()));

		return str.toString();
	}

}
